/*
 * twi.c
 *
 * Created: 2012.11.22. 14:02:28
 *  Author: some00
 */ 
#define START 0x08
#define MT_SLA_ACK	0x18
#define MT_DATA_ACK 0x28
#define REPEATED_START 0x10
#define MR_SLA_ACK 0x40
#define MR_DATA_ACK 0x50
#define MR_DATA_NOT_ACK 0x58

#define CHECK_BIT(var, pos) ((var) & (1<<(pos)))

#include <avr/io.h>
#include "twi.h"

//0: busy
//1: free
volatile int busState;

ISR(INT1_vect)
{
	if ( busState	&& CHECK_BIT(PIND, 4)	) busState = 1;
	if ( !busState	&& !CHECK_BIT(PIND, 4)	) busState = 0;
}

void InitTwi()
{
	for (int i = 0; i < 8; i++) data_received[i] = 0;
	
	//Enable INT1 - S_P_INT
	EICRA &=	~( 1 << 3 );
	EICRA |=	( 1 << 2 );
	EIMSK |=	( 1 << 1 );
	
	//Enable DDC - PC_MON
	DDRD |= (1<<PORTD5);
	PORTD |= (0<<PORTD5);
	
	busState = 0;
	
	TWBR = 72;							//(CPU_F - 16 * SCL_F)/(2*SCL_F)
	TWCR = (0 << TWIE) | (0<<TWEN);		//TWI Disable, TWI Interrupt disable
	TWSR = 0x00;						//TWI Prescaler 00 => 1 prescale
}

void enableMaster()
{
	while(busState == 1);
	PORTD |= (1<<PORTD5);
	//_delay_ms(20);
	TWCR = (1<<TWEN) | (0<<TWIE);
}


void disableMaster()
{
	while(busState == 1);
	PORTD |= (0<<PORTD5);
	TWCR = (0<<TWEN) | (0<<TWIE);
}

int TWI_READ(int address)
{
	//START
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != START) return (TWSR & 0xF8);
	//SLA+W
	TWDR = 0x6E;
	TWCR = (1<<TWINT) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != MT_SLA_ACK) return (TWSR & 0xF8);
	
	for( int i = 0; i < 5 ; i++)
	{
		//DATA_SEND
		if ( i == 0) TWDR = 0x51;
		if ( i == 1) TWDR = 0x82;
		if ( i == 2) TWDR = 0x01;
		if ( i == 3) TWDR = address;
		if ( i == 4) TWDR = 0x6E^0x51^0x82^0x01^address;
		
		TWCR = (1<<TWINT) | (1<<TWEN);
		//WAIT
		while(!(TWCR & (1<<TWINT)));
		if((TWSR & 0xF8) != MT_DATA_ACK) return (TWSR & 0xF8);
	}
	
	
	//REPEATED_START
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != REPEATED_START) return (TWSR & 0xF8);
	//SLA+R
	TWDR = 0x6F;
	TWCR = (1<<TWINT) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != MR_SLA_ACK) return (TWSR & 0xF8);
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<< TWEA);
	
	for( int i = 0; i < 9 ; i++)
	{	
		//WAIT
		while(!(TWCR & (1<<TWINT)));
		if((TWSR & 0xF8) != MR_DATA_ACK) return (TWSR & 0xF8);
		
		//DATA_RECEIVE
		data_received[i] = TWDR;
		//if (i == 1 && data_received[1] == 0x80) USART_Transmit('U');
		if (i != 8) TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
		else TWCR = (1<<TWINT) | (1<<TWEN) | (0<<TWEA);
	}
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != MR_DATA_NOT_ACK) return (TWSR & 0xF8);
	data_received[9] = TWDR;
	
	//STOP
	TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO);
	return 0;
}

int TWI_MODIFY(int address, int value)
{
	//START
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != START) return (TWSR & 0xF8);
	//SLA+W
	TWDR = 0x6E;
	TWCR = (1<<TWINT) | (1<<TWEN);
	//WAIT
	while(!(TWCR & (1<<TWINT)));
	if((TWSR & 0xF8) != MT_SLA_ACK) return (TWSR & 0xF8);
	
	for( int i = 0; i < 7 ; i++)
	{
		//DATA_SEND
		switch(i)
		{
			case 0: TWDR = 0x51;		break;
			case 1: TWDR = 0x84;		break;
			case 2: TWDR = 0x03;		break;
			case 3: TWDR = address;		break;
			case 4: TWDR = 0x00;		break;
			case 5: TWDR = value;		break;
			case 6: TWDR = 0x6E^0x51^0x84^0x03^address^0x00^value; break;
		}		
		
		TWCR = (1<<TWINT) | (1<<TWEN);
		//WAIT
		while(!(TWCR & (1<<TWINT)));
		if((TWSR & 0xF8) != MT_DATA_ACK) return (TWSR & 0xF8);	 
	}
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
	return 0;
}

void sendNextCommand()
{
	if( bufferEmpty() ) return;
	
	Operation op;
	bufferGet(&op);
	
	enableMaster();
	
	if (op.FlashType == 'D')
	{
		if (op.OpType == 'R')
		{
			int error = TWI_READ(op.address);
			
			//USART_putInt(op.address);
			//USART_putInt(data_received[4]);
			//USART_putInt(error);
			//USART_Transmit('\n');
			
			//error -> transmit STOP
			if (error) 
			{
				TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
			}				
			
			int  tryCount = 0;
			while(data_received[4] != op.address) 
			{
				if(tryCount > 4) break;
				_delay_ms(20);
				error = TWI_READ(op.address);
				
				//error -> transmit STOP
				if (error) TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
				
				//USART_Transmit('W');					
				//USART_putInt(op.address);
				//USART_putInt(data_received[4]);
				//USART_putInt(error);
				//USART_Transmit('\n');
				
				tryCount++;
			}
			
			//_delay_ms(200);
			if(!(tryCount > 4))
			{
				op.value = data_received[9];
			
				//callback
				op.callback(op);
			}			
		}
		
		if (op.OpType == 'W')
		{
			int  error = TWI_MODIFY(op.address, op.value);
			while(error)
			{
				TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
				error = TWI_MODIFY(op.address, op.value);
			}
			
			_delay_ms(50);
			//USART_Transmit('R');					
			//USART_putInt(op.address);
			//USART_putInt(data_received[4]);
			//USART_putInt(error);
			//USART_Transmit('\n');
		}
	}
	
	disableMaster();
}